import pygame
import random


"""
步骤：
1、创建基础窗口
    窗口大小、标题、颜色
    
2、绘制图形
    首先绘制一个图形
    然后添加颜色、单元格大小、初始化位置
    绘制多个图形

3、图形下落
    图形往下移动
    控制速度

4、停止
    落底停止
    接触停止
    一个停止后，生成新的一个

5、旋转与左右移动
    图形变换
    左右移动

6、消除

7、得分并展示
"""



# 方块颜色
colors = [
    (0, 240, 240),
    (240, 0, 0),
    (0, 240, 0),
    (0, 0, 240),
    (240, 160, 0),
    (160, 0, 240),
    (240, 240, 0),
]

pygame.init()
screen_width = 12
screen_height = 20
screen_color = (0, 0, 0)
zoom = 30
size = (
    screen_width * zoom,
    screen_height * zoom
)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("俄罗斯方块")

done = False
clock = pygame.time.Clock()
fps = 25


class Figure:
    """方块类"""
    x = 0
    y = 0
    # 方块形状,方块旋转后的形状，二维坐标转一维坐标结果，4*4的坐标系
    # 定义一个二维列表，包含了多个图形的坐标点
    # 每个子列表代表一个图形，其中的每个小列表包含了一组位置
    figures = [
        [[1, 5, 9, 13], [4, 5, 6, 7]],
        [[4, 5, 9, 10], [2, 6, 5, 9]],
        [[6, 7, 9, 10], [1, 5, 6, 10]],
        [[1, 2, 5, 9], [0, 4, 5, 6], [1, 5, 9, 8], [4, 5, 6, 10]],
        [[1, 2, 6, 10], [5, 6, 7, 9], [2, 6, 10, 11], [3, 5, 6, 7]],
        [[1, 4, 5, 6], [1, 4, 5, 9], [4, 5, 6, 9], [1, 5, 6, 9]],
        [[1, 2, 5, 6]],
    ]

    def __init__(self, x, y):
        """
        初始化对象的属性。

        初始化对象的位置（x, y），类型，颜色和初始旋转状态。
        类型和颜色通过随机数生成，确保每个对象在创建时具有独特的特征。

        参数:
        x - 对象的x坐标
        y - 对象的y坐标
        """
        self.x = x
        self.y = y
        # 随机确定对象的类型
        self.type = random.randint(0, len(self.figures) - 1)
        # 随机确定对象的颜色
        self.color = random.randint(1, len(colors) - 1)
        self.rotation = 0  # 初始化对象的旋转状态为0度

    def image(self):
        """
        获取对象的图像表示。

        根据对象的类型和旋转状态，从一个内部存储的图像字典中检索对应的图像。
        这个方法使得对象可以根据其状态动态地显示不同的图像，而不需要直接修改图像数据。

        Returns:
            图像数据，具体格式取决于存储方式，可能是图像文件路径、图像对象等。
        """
        # 根据对象的类型和旋转状态获取对应的图像
        return self.figures[self.type][self.rotation]

    def rotate(self):
        """
        旋转当前图形。
        该方法用于实现图形的旋转功能，每调用一次，图形就会按照预设的旋转步长旋转一定角度。
        旋转操作基于图形的当前旋转状态和图形类型进行。

        注意事项：
        - 旋转操作不会改变图形的本质，只是改变了其在屏幕上的展示方向。
        - 旋转是以图形类型为基础的，不同类型的图形可能有不同的旋转方式。
        """
        # 更新图形的旋转状态，确保旋转角度在合法范围内
        self.rotation = (self.rotation + 1) % len(self.figures[self.type])


# 创建对象
class Tetris:
    # 初始化全局变量，用于控制绘图的初始层级和位置
    level = 1
    # 初始化绘图的起始位置
    x = y = 0
    # 定义缩放因子，用于控制绘图的细节级别
    zoom = 30  # 方块大小
    # 初始化图形对象变量，用于后续的绘图操作
    figure = None
    score = 0

    def __init__(self, height, width):
        self.height = height
        self.width = width

        self.field = []
        self.state = "start"
        for i in range(height):
            new_line = []
            for j in range(width):
                new_line.append(0)
            self.field.append(new_line)

    def new_figure(self):
        self.figure = Figure(3, 0)

    def intersects(self):
        """
        检查当前图形是否与游戏场地上已有方块相交。
        该方法通过遍历当前图形的每一个单元格，然后检查这个单元格在游戏场地上的位置是否超出边界，
        或者是否已有方块占据该位置。如果发现任何相交情况，立即返回True，表示相交发生；如果遍历完
        所有单元格都没有发现相交，返回False，表示没有相交。

        返回:
            bool: 如果当前图形与游戏场地相交，则返回True；否则返回False。
        """
        # 初始化一个标志变量，用于判断是否存在相交情况
        """判断是否到底"""
        intersection = False

        # 遍历当前图形的每一个单元格
        for i in range(4):
            for j in range(4):
                # 检查当前单元格在图形中的位置是否非空
                if i * 4 + j in self.figure.image():
                    # 检查当前单元格在游戏场上的位置是否超出边界，或者已有方块占据该位置
                    if i + self.figure.y > self.height - 1 or \
                            j + self.figure.x > self.width - 1 or \
                            j + self.figure.x < 0 or \
                            self.field[i + self.figure.y][
                                j + self.figure.x] > 0:
                        # 如果存在相交情况，设置标志变量为True，并立即退出循环
                        intersection = True
                        break
        # 返回是否存在相交情况
        return intersection

    def break_lines(self):
        """消除"""
        lines = 0
        for i in range(1, self.height):
            zeros = 0
            for j in range(self.width):
                if self.field[i][j] == 0:
                    zeros += 1
            if zeros == 0:
                lines += 1
                self.score += 1
                for i1 in range(i, 1, -1):
                    for j in range(self.width):
                        self.field[i1][j] = self.field[i1 - 1][j]
                pygame.display.set_caption("俄罗斯方块   分数：{}".format(self.score))

    def go_space(self):
        """一键落下"""
        while not self.intersects():
            self.figure.y += 1
        self.figure.y -= 1
        self.freeze()

    def go_down(self):
        self.figure.y += 1
        if self.intersects():
            self.figure.y -= 1
            self.freeze()
            if self.figure.y == 1:
                self.state = "gameover"

    def freeze(self):
        """
        将当前落下的方块固定到游戏场中。

        遍历当前方块的图像，如果某个方块的位置在游戏场内，则将该位置的颜色设置为方块的颜色。
        固定方块后，检查并消除完整的一行，然后生成新的方块。
        如果新生成的方块与已有方块重叠，则游戏结束。
        """
        # 遍历方块的每个可能位置
        """触底之后留住方块"""
        for i in range(4):
            for j in range(4):
                # 判断当前位置是否在当前方块的图像中
                if i * 4 + j in self.figure.image():
                    # 将该位置的颜色设置为当前方块的颜色
                    self.field[i + self.figure.y][j + self.figure.x] \
                        = self.figure.color
        # 检查并消除游戏场中完整的行
        self.break_lines()
        # 生成新的方块
        self.new_figure()
        # 检查新方块是否与已有方块重叠
        if self.intersects():
            # 如果重叠，则游戏结束
            self.state = "gameover"

    def go_side(self, dx):
        """
        尝试移动图形沿x轴。

        参数:
        dx -- x轴方向上的移动量。

        该方法尝试将图形向右（如果dx为正）或向左（如果dx为负）移动dx个单位。
        如果移动后图形与其它对象发生交叉或重叠，则移动无效，图形位置保持不变。
        """
        # 保存当前图形的x坐标
        old_x = self.figure.x
        # 尝试更新图形的x坐标
        self.figure.x += dx
        # 检查移动后是否发生交叉或重叠
        if self.intersects():
            # 如果发生交叉或重叠，恢复图形的原始x坐标
            self.figure.x = old_x

    def rotate(self):
        old_rotation = self.figure.rotation
        self.figure.rotate()
        if self.intersects():
            self.figure.rotation = old_rotation


pressing_down = False
counter = 0
game = Tetris(screen_height, screen_width)
while not done:
    if game.figure is None:
        game.new_figure()

    counter += 1
    if counter > 100000:
        counter = 0
    print("counter:{}".format(counter))
    if counter % (fps // game.level // 2) == 0 or pressing_down:
        if game.state == "start":
            game.go_down()
            print("go_down after figure y====={}".format(game.figure.y))

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                game.rotate()
            if event.key == pygame.K_DOWN:
                pressing_down = True
            if event.key == pygame.K_LEFT:
                game.go_side(-1)
            if event.key == pygame.K_RIGHT:
                game.go_side(1)
            if event.key == pygame.K_SPACE:
                game.go_space()
            # if event.key == pygame.K_ESCAPE:
            #     game.__init__(screen_height, screen_width)
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_DOWN:
                pressing_down = False

    screen.fill(screen_color)

    for i in range(game.height):
        for j in range(game.width):
            if game.field[i][j] > 0:
                pygame.draw.rect(screen, colors[game.field[i][j]], [
                    game.x + game.zoom * j + 1,
                    game.y + game.zoom * i + 1, game.zoom - 2, game.zoom - 1])

    if game.figure is not None:
        print("figure====={}".format(game.figure.image()))
        for i in range(4):
            for j in range(4):
                p = i * 4 + j
                if p in game.figure.image():
                    rect = pygame.rect = (
                        game.x + zoom * (j + game.figure.x) + 1,
                        game.y + zoom * (i + game.figure.y) + 1, zoom - 2, zoom - 2)
                    print("rect====={}".format(rect))
                    pygame.draw.rect(screen, colors[game.figure.color], rect)

    pygame.display.flip()
    clock.tick(fps)

pygame.quit()
